package com.easyskool.dao;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.PropertyProjection;
import org.hibernate.criterion.Restrictions;
import org.hibernate.exception.ConstraintViolationException;

import com.easyskool.hibernate.config.HibernateUtil;
import com.easyskool.utilfunction.NullObjects;
import com.ivp.cloud.stratus.logger.LoggingUtil;

@SuppressWarnings({ "unchecked", "rawtypes" })
public class BaseDao extends HibernateUtil {

	private static LoggingUtil logger = (LoggingUtil) LoggingUtil
			.getLogger(BaseDao.class.getName());

	public List customQuery(String queryString,HashMap<String,Object> params) throws Exception
	{
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		List list=new ArrayList<String>();
		try {
			Query query = session.createQuery(queryString);
			Iterator iter = params.keySet().iterator();
			while(iter.hasNext()) {  
				String  key = (String)iter.next(); 
				Object val =  params.get(key); 
				query.setParameter(key, val);
				System.out.println("key,val: " + key + "," + val);
				}
			
			  list = query.list();
			
		} catch (Exception e) {
			session.getTransaction().rollback();
e.printStackTrace();
		}
//		session.getTransaction().commit();
		logger.info("Record updated");
		
			return list;
	}
	
	public int customQueryCUD(String queryString,HashMap<String,Object> params) throws Exception
	{
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		int res=0;
		try {
			Query query = session.createQuery(queryString);
			Iterator iter = params.keySet().iterator();
			while(iter.hasNext()) {  
				String  key = (String)iter.next(); 
				Object val =  params.get(key); 
				query.setParameter(key, val);
				System.out.println("key,val: " + key + "," + val);
				}
			
			res=query.executeUpdate();	
		} catch (Exception e) {
			session.getTransaction().rollback();
e.printStackTrace();
		}
		session.getTransaction().commit();
		logger.info("Record updated");
		
			return res;
	}
	
	public List distinct(Class classObject, Map<String, Object> whereMap,
			Set<String> projection) throws Exception {
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		List<Object> studentList = null;
		try {
			if (null != classObject) {
				Criteria cr = session.createCriteria(classObject);
				if (whereMap.size() > 0) {
					Set<String> keys = whereMap.keySet();
					Iterator<String> itr = keys.iterator();
					while (itr.hasNext()) {
						String key = itr.next();
						Criterion crx = Restrictions.eq(key, whereMap.get(key));
						cr.add(crx);
					}
				}

				ProjectionList proList = Projections.projectionList();
				if (!NullObjects.isNull(projection)) {
					for (String proj : projection) {
						Projection pro = Projections.property(proj);
						Projection proje = Projections.distinct(pro);
						proList.add(proje);

					}
					cr.setProjection(proList);
				}

				studentList = (List<Object>) cr.list();
			}
		}   catch (Exception e) {
			session.getTransaction().rollback();
e.printStackTrace();
		}
		session.getTransaction().commit();

		logger.info("Retrieved List : " + studentList);
		return studentList;
	}

	/**
	 * get the data from db based on projections
	 * 
	 * @param classObject
	 * @param whereMap
	 * @param projection
	 * @return List// select with projections
	 * @throws Exception
	 */
	public List projection(Class classObject, Map<String, Object> whereMap,
			Set<String> projection) throws Exception {
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		List<Object> studentList = null;
		try {
			if (null != classObject) {
				Criteria cr = session.createCriteria(classObject);
				if (!NullObjects.isNull(whereMap)) {
					if (whereMap.size() > 0) {
						Set<String> keys = whereMap.keySet();
						Iterator<String> itr = keys.iterator();
						while (itr.hasNext()) {
							String key = itr.next();
							Criterion crx = Restrictions.eq(key,
									whereMap.get(key));
							cr.add(crx);
						}
					}
				}
				System.out.println("cr 1 : " + cr);
				ProjectionList proList = Projections.projectionList();
				if (!NullObjects.isNull(projection)) {
					for (String proj : projection) {
						PropertyProjection proje = Projections.property(proj);
						proList.add(proje);

					}
					cr.setProjection(proList);
				}

				studentList = (List<Object>) cr.list();
			}
		}  catch (Exception e) {
			session.getTransaction().rollback();
e.printStackTrace();
		}

		session.getTransaction().commit();

		System.out.println("student List : " + studentList);
		return studentList;
	}

	/**
	 * Get data from db in pojo json property order
	 * 
	 * @param classObject
	 * @param whereMap
	 * @return
	 * @throws Exception
	 */
	public List find(Class classObject, Map<String, Object> whereMap)
			throws Exception {
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		List<Object> retrievedList = null;
		try {
			if (null != classObject) {
				Criteria cr = session.createCriteria(classObject);
				if (!NullObjects.isNull(whereMap)) {
					Set<String> keys = whereMap.keySet();
					Iterator<String> itr = keys.iterator();
					while (itr.hasNext()) {
						String key = itr.next();
						Criterion crx = Restrictions.eq(key, whereMap.get(key));
						cr.add(crx);
					}
				}

				retrievedList = (List<Object>) cr.list();
			}
		}  catch (Exception e) {
			session.getTransaction().rollback();

			e.printStackTrace();
		}

		logger.info("Retrieved List : " + retrievedList);
		return retrievedList;
	}

	/**
	 * updates a record in a table
	 * 
	 * @param classObject
	 *            : name of the table
	 * @param whereClause
	 *            : where statements
	 * @param updateClause
	 *            : fields to be updated
	 * @return: number of rows updated
	 * 
	 * @throws Exception
	 */
	public void update(Class classObject, Map<String, Object> whereClause,
			String updateClause) throws  Exception {
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try {
			ObjectMapper mapper = new ObjectMapper();

			Object obj = classObject.newInstance();
			obj = mapper.readValue(updateClause, classObject);

			session.merge(obj);
		} catch (Exception e) {
			session.getTransaction().rollback();
e.printStackTrace();
		}
		session.getTransaction().commit();
		logger.info("Record updated");
		return;
	}

	/**
	 * updates a record in a table
	 * 
	 * @param classObject
	 *            : name of the table
	 * @param whereClause
	 *            : where statements
	 * @param updateClause
	 *            : fields to be updated
	 * @return: number of rows updated
	 * @throws Exception
	 */
	public int updateWithoutFullobject(Class classObject,
			Map<String, Object> whereClause, Map<String, Object> updateClause)
			throws Exception {
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		int result = 0;

		try {
			if (null != classObject) {
				String classString = classObject.getSimpleName();
				StringBuilder queryBuilder = new StringBuilder("update "
						+ classString + " set ");
				if (updateClause.size() > 0) {
					Set<String> keys = updateClause.keySet();
					Iterator<String> itr = keys.iterator();
					while (itr.hasNext()) {
						String key = itr.next();
						queryBuilder.append(key + "= :" + (key) + "set ");
						if (itr.hasNext()) {
							queryBuilder.append(",");
						}
					}
				}
				if (whereClause.size() > 0) {
					queryBuilder.append(" where ");
					Set<String> keys = whereClause.keySet();
					Iterator<String> itr = keys.iterator();
					while (itr.hasNext()) {
						String key = itr.next();
						queryBuilder.append(key + "=  :" + (key) + "where ");
						if (itr.hasNext()) {
							queryBuilder.append(" and ");
						}
					}
				}

				Query query = session.createQuery(queryBuilder.toString());
				// For the update clause

				if (updateClause.size() > 0) {
					Set<String> keys = updateClause.keySet();
					Iterator<String> itr = keys.iterator();
					while (itr.hasNext()) {
						String key = itr.next();

						query.setParameter(key + "set", updateClause.get(key));

						if (itr.hasNext()) {
							queryBuilder.append(",");
						}
					}
				}
				// fr the where clause
				if (whereClause.size() > 0) {

					Set<String> keys = whereClause.keySet();
					Iterator<String> itr = keys.iterator();
					while (itr.hasNext()) {
						String key = itr.next();

						query.setParameter(key + "where", whereClause.get(key));
						if (itr.hasNext()) {
							queryBuilder.append(" and ");
						}
					}
				}

				result = query.executeUpdate();
			}
		} catch (Exception e) {
			session.getTransaction().rollback();
e.printStackTrace();
		}
		session.getTransaction().commit();
		logger.info("Record updated");
		return result;
	}

	public void update(Class classObject, Map<String, Object> whereClause,
			Map updateClause) throws Exception {
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try {
			ObjectMapper mapper = new ObjectMapper();
			Object obj = classObject.newInstance();
			obj = mapper.convertValue(updateClause, classObject);

			session.merge(obj);
		} catch ( Exception e) {
			session.getTransaction().rollback();
			e.printStackTrace();
		}  

		session.getTransaction().commit();
		logger.info("Record Updated");
	}

	/**
	 * deletes a record in a table
	 * 
	 * @param classObject
	 *            : name of the table
	 * @param whereClause
	 *            : where statements
	 * @return: number of rows deleted
	 * @throws Exception
	 */
	public int delete(Object classObejct) throws Exception {
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		int result = 0;
		try {
			session.delete(classObejct);
		} catch (Exception e) {
			session.getTransaction().rollback();
			e.printStackTrace();
		}
		session.getTransaction().commit();
		System.out.println("student List : " + result);
		return result;

	}

	/**
	 * Delete using where clause, object not required
	 * 
	 * @param classObject
	 * @param whereClause
	 * @return
	 * @throws Exception
	 */
	public int deleteWithoutFullObject(Class classObject,
			Map<String, Object> whereClause) throws Exception {
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		int result = 0;
		try {
			if (null != classObject) {
				String classString = classObject.getSimpleName();
				StringBuilder queryBuilder = new StringBuilder("delete "
						+ classString + " where ");

				if (whereClause.size() > 0) {
					Set<String> keys = whereClause.keySet();
					Iterator<String> itr = keys.iterator();
					while (itr.hasNext()) {
						String key = itr.next();
						queryBuilder.append(key + "= :" + (key) + "");
						if (itr.hasNext()) {
							queryBuilder.append(" and ");
						}
					}
				}
				Query query = session.createQuery(queryBuilder.toString());
				if (whereClause.size() > 0) {

					Set<String> keys = whereClause.keySet();
					Iterator<String> itr = keys.iterator();
					while (itr.hasNext()) {
						String key = itr.next();

						query.setParameter(key, whereClause
								.get(key).toString());

						if (itr.hasNext()) {
							queryBuilder.append(" and ");
						}
					}
				}

				result = query.executeUpdate();
			}
		} catch (Exception e) {
			session.getTransaction().rollback();

		e.printStackTrace();
		}
		session.getTransaction().commit();
		System.out.println("student List : " + result);
		return result;

	}

	/**
	 * Multiple delete using WHERE 'x' IN ()
	 * 
	 * @param classObject
	 * @param whereClause
	 * @return
	 * @throws Exception
	 */
	public int batchDelete(Class classObject, Map<String, Object> whereClause)
			throws Exception {
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		int result = 0;
		String replaceString = null;
		try {
//			for (Map.Entry<String, Object> entry : whereClause.entrySet()) {
//				System.out.println("Key : " + entry.getKey() + " Value : "
//					+ entry.getValue());
//				System.out.println("Key : " + entry.getKey() + " Value : "
//						+ entry.getValue().toString().trim());
//				entry.setValue((Object)entry.getValue().toString().trim());
//			}
			if (null != classObject) {
				String classString = classObject.getSimpleName();
				StringBuilder queryBuilder = new StringBuilder("delete "
						+ classString + " where ");

				if (whereClause.size() > 0) {
					Set<String> keys = whereClause.keySet();
					Iterator<String> itr = keys.iterator();
					while (itr.hasNext()) {
						String key = itr.next();

						List<String> list = (List<String>) whereClause.get(key);
						String ss = null;
						
						replaceString = "(";
						int count=0;
						
						for(String str : list){
							ss = str.trim();
							replaceString += "'" + ss.trim() + "'";
							if(count++ < list.size()-1) {
								replaceString += ",";
							}
						}
						replaceString += ")";
						
						queryBuilder.append(key + " IN " + replaceString);
						if (itr.hasNext()) {
							queryBuilder.append(" and ");
						}
					}
				}

				Query query = session.createQuery(queryBuilder.toString());
				result = query.executeUpdate();
			}
		} catch (Exception e) {
			session.getTransaction().rollback();
e.printStackTrace();
		}
		session.getTransaction().commit();
		System.out.println("student List : " + result);
		return result;

	}

//	/**
//	 * Insert new user in accessAuthorization
//	 * 
//	 * @param userToInsert
//	 * @throws Exception
//	 */
//	public void insertUser(AccessAuthorization userToInsert)
//			throws Exception {
//		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
//		session.beginTransaction();
//		try {
//			session.save(userToInsert);
//		} catch (ConstraintViolationException e) {
//			session.getTransaction().rollback();
//
//			Exception d = new Exception("Id already exists", e);
//			logger.error(e.getMessage() + ":" + e.getCause());
//			throw d;
//		} catch (Exception e) {
//			session.getTransaction().rollback();
//
//			Exception d = new Exception("", e);
//			logger.error(e.getMessage() + ":" + e.getCause());
//			throw d;
//		}
//
//		session.getTransaction().commit();
//		logger.info("Record saved");
//	}

	/**
	 * Insert using pojo object
	 * 
	 * @param pojo
	 * @param toInsert
	 * @throws Exception
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 */
	public void insert(Class pojo, String toInsert) throws Exception {
		Object obj = null;
		
		try {
			ObjectMapper mapper = new ObjectMapper();

			obj = pojo.newInstance();

			obj = mapper.readValue(toInsert, pojo);
		} catch (Exception e) {
			logger.error(e.getMessage() + ":" + e.getCause());

			e.printStackTrace();;
		}

		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		try {
			session.save(obj);
		}   catch (Exception e) {
			session.getTransaction().rollback();
			e.printStackTrace();
		}

		session.getTransaction().commit();
		logger.info("Record Saved");
	}

	/**
	 * Insert using pojo object from Map
	 * 
	 * @param pojo
	 * @param toInsert
	 * @throws JsonParseException
	 * @throws JsonMappingException
	 * @throws IOException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws Exception
	 */
	public void insert(Class pojo, Map<String, Object> toInsert)
			throws JsonParseException, JsonMappingException, IOException,
			InstantiationException, IllegalAccessException, Exception {
		ObjectMapper mapper = new ObjectMapper();

		Object obj = pojo.newInstance();
		try {

			obj = mapper.convertValue(toInsert, pojo);
		} catch (Exception e) {
			e.printStackTrace();
			}

		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		try {
			session.save(obj);
		}  catch (Exception e) {
			session.getTransaction().rollback();
			e.printStackTrace();
			}
		session.getTransaction().commit();
		logger.info("Record saved");
	}

	/**
	 * Insert and get autogenerated primary key
	 * 
	 * @param pojo
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public <T> T insertWithObject(Class<T> pojo, T obj) throws Exception {
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		try {
			  session.save(obj);

		} catch (ConstraintViolationException e) {
			session.getTransaction().rollback();
			e.printStackTrace();
		} catch (Exception e) {
			session.getTransaction().rollback();
e.printStackTrace();
		}

		session.getTransaction().commit();
		logger.info("Record saved");
		return obj;
	}

}
